%{
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "parse_code.h"
#define YYERROR_VERBOSE 0
int yylex();
void yyerror(const char *s);
#define GAPCODE 256
static int  maxcurr = GAPCODE;
static Byte *bp;
static Word cursor = 0;

//临时保存栈和游标
static Byte *tmp_bp;
static Word tmp_cursor = 0;

void put_code(Byte d){
    if(cursor == maxcurr){
        maxcurr += GAPCODE;
        bp = (Byte *)realloc(bp, maxcurr*sizeof(Byte));
    }

    bp[cursor++] = d;
}

void put_data_w(Word data){
    CodeWord d;
    d.w = data;
    put_code(d.data[0]);
    put_code(d.data[1]);
    
}

void put_data_n(double data){
    CodeNumber d;
    d.n = data;
    for(int i=0; i< sizeof(double); i++) put_code(d.data[i]);    
}

void put_variable(int d){
    if(d < 0) return;
    put_code(PUSH_NAME);
    put_data_w(d);
}
%}
%union{
    unsigned short  p_name;
    unsigned short  p_word;
    unsigned short  p_str;
    double  p_num;
    unsigned char   p_argc;
    int    p_int;
}

%token <p_name> NAME
%token <p_str> STRING
%token <p_num> NUMBER


%token EQ NEQ GT LT GE LE IDENTITY NIDENTITY

%left AND OR
//    ==,!=, >, <, >=,<=
%left EQ NEQ GT LT GE LE 
%left '='
%left '+' '-'
%left '*' '/'
%nonassoc UMINUS

%token INT
/* 声明类型 */
%token NUM STR BOOLEAN ARR DICT 
/* 循环 */
%token DO WHILE FOR BREAK CONTINUE LOOP
/* 判断 */
%token IF ELSE ELSEIF SWITCH CASE DEFAULT
/* 函数 */
%token FUNC RETURN PRINT THEN END TO
%type <p_int> expr variable jmp_tag if_else arr_def arr_def1
%type <p_argc> argv_list argv_list1 
%%

start: syntax_statement 
    | start syntax_statement
    ;
syntax_statement: body
    | function
    ;
function : FUNC NAME {
        put_code(PUSH_DEF_FUNC); 
        put_data_w($2);
        put_data_w(0); //所占字节长度
    } jmp_tag { $<p_word>$=cursor; } '(' argv_list ')' {
        put_code(PUSH_RESET_ARGV);
        put_code($7);
    } start_body END FUNC return_type {
        put_code(PUSH_REMOVE_STACK);
        // 注意定义函数起始地址一定要+3 (绕过jmp_tag);
        bp[$4] = PUSH_JMP;
        *(Word *)(&(bp[$4+1])) = cursor-$4-3;
        *(Word *)(&(bp[$4-2])) = cursor-($4+sizeof(Word)+1);

    }
    ;
argv_list: /* empty */ { $$=0;}
    | argv_list1 { $$=$1; }
    ;
argv_list1: data_type_must NAME { put_data_w($2); $$=1;}
    | argv_list ',' data_type_must NAME { put_data_w($4); $$ +=1;}
    ;
body: expr ';'
    | if_syntax
    | while_syntax
    | loop_syntax
    | return_syntax ';'
    ;
start_body: body
    | start_body body
    ;
if_syntax: IF expr THEN jmp_tag start_body jmp_tag if_else END IF{
        //在读取字节码的时候cur会+3;所以下面跳转计算要-3
        if($7 > 0){ //有else的时候
            //expr=true,  执行完start_body后执行jmp_tag跳过else
            bp[$6] = PUSH_JMP;
            *(Word *)(&(bp[$6+1])) = $7-$6-3;
        }
        //expr=false, 跳过 start_body+jmp_tag执行else(有的话)
        //没有else就正常向下执行
        bp[$4] = PUSH_IF_JMP;
        //*(Word *)(&(bp[$4+1])) = ($6+sizeof(Word)+1)-$4;
        //本来需要+jmp_tag(jmp_tag占3字节), 但又要-3, 刚好抵消; 
        *(Word *)(&(bp[$4+1])) = $6-$4;
    }
    ;
if_else: /* empty */ { $$=0; }
    | ELSE start_body { $$=cursor;}
    | ELSEIF expr THEN jmp_tag start_body jmp_tag if_else {
        if($7 > 0){
            bp[$6] = PUSH_JMP;
            *(Word *)(&(bp[$6+1])) = $7-$6-3;
        }
        bp[$4] = PUSH_IF_JMP;
        *(Word *)(&(bp[$4+1])) = $6-$4;
        $$=$7;
    }
    ;
while_syntax: WHILE { $<p_word>$=cursor;} expr THEN jmp_tag start_body jmp_tag END WHILE{
        
        bp[$5] = PUSH_IF_JMP;
        *(Word *)(&(bp[$5+1])) = $7-$5; // 跳过 start_body+jmp_tag
        
        bp[$7] = PUSH_SUB_JMP;
        *(Word *)(&(bp[$7+1])) = cursor-$<p_word>2;
    }
    ;
loop_syntax: LOOP '.' variable '=' expr THEN start_body jmp_tag END LOOP{}
    ;
return_syntax: RETURN expr { put_code(PUSH_REMOVE_STACK); }
    ;
return_type:/* empty */ { put_code(RET_NULL); }
    | TO NUM { put_code(RET_NUM); }
    | TO STR  {  put_code(RET_STR); }
    | TO BOOLEAN { put_code(RET_BOOL); }
    ;
jmp_tag:/* empty */ { 
        $$ = cursor;
        //占位符, 后面将改为具体的操作码和操作数
        put_code(PUSH_NOP);
        put_data_w(0);
    }
    ;
assign_value:/* empty */ {  }
    | assign_value_1 {  }
    ;
assign_value_1 : '=' expr { put_code(PUSH_ASSIGN);  }
    | '=' arr_set { put_code(PUSH_ARRAY_SET); }
    ;
data_type_must: NUM { put_code(PUSH_CREATE_NUM);  }
    | STR { put_code(PUSH_CREATE_STR);  }
    | BOOLEAN { put_code(PUSH_CREATE_BOOL);  }
    ;
arr_def: /* empty */ { $$=0; }
    | arr_def1 { $$=$1; }
    ;
arr_def1: '[' data_type_2 ']' { $$=1; }
    | arr_def1 '[' data_type_2 ']' { $$+=1; }
    ;
arr_set:  '[' list ']'
    ;
list: expr { }
    | list ',' expr { }
    ;
expr: expr '-' expr 	{ put_code(PUSH_SUB); }
	| expr '+' expr 	{ put_code(PUSH_ADD); }
	| expr '*' expr 	{ put_code(PUSH_MUL); }
	| expr '/' expr 	{ put_code(PUSH_DIV); }
	| expr EQ  expr 	{ put_code(PUSH_COMPARISON_EQ); }
	| expr NEQ expr 	{ put_code(PUSH_COMPARISON_NEQ); }
	| expr GT  expr 	{ put_code(PUSH_COMPARISON_GT); }
	| expr LT  expr 	{ put_code(PUSH_COMPARISON_LT); }
	| expr GE  expr 	{ put_code(PUSH_COMPARISON_GE); }
	| expr LE  expr 	{ put_code(PUSH_COMPARISON_LE); }
	| expr AND jmp_tag expr {
        bp[$3] = PUSH_COMPARISON_AND;
        bp[$3+1] = cursor-$3;
    }
	| expr OR jmp_tag expr 	{ 
        bp[$3] = PUSH_COMPARISON_OR;
        bp[$3+1] = cursor-$3;
    }
	| '-' expr %prec UMINUS  { }
	| '(' expr ')' 		{  }
	| data_type            {  }
	| data_type_must NAME { put_data_w($2); } arr_def { 
        if($4>0){ 
            put_code(PUSH_CREATE_ARRAY);
            put_data_w($4);
        } 
    } assign_value { $$=-1; }
	| func_call            {  }
	| variable          {put_variable($1); $$=-1;}
	| variable {put_variable($1); } assign_value_1 { $$=-1;}
	;
data_type: NUMBER       { 
        put_code(PUSH_NUMBER);
        put_data_n($1);
    }
	| STRING			{ 
        put_code(PUSH_STRING);
        put_data_w($1);
    }
    ;
data_type_2: NUMBER       {  put_code(PUSH_NUMBER); put_data_n($1); }
    | variable			{  put_variable($1); }
    ;
params_list:/* empty */ { }
    | expr { }
    | params_list ',' expr { }
    ;
func_call :  variable {put_variable($1); put_code(PUSH_FUNC_TAG); } '(' params_list ')' { put_code(PUSH_FUNC_CALL); }
    ;
variable : NAME { $$ = $1; }
    | NAME {put_variable($1); } arr_def1 { 
        put_code(PUSH_ARRAY);
        put_data_w($3);
        $$ = -1;
    }
    | variable '.'  NAME {
        put_variable($1);
        put_code(PUSH_INSIDE_NAME);
        put_data_w($3);
        $$ = -1;
    }
    ;
%%



void yyerror(const char *s){
    fprintf(stdout, "%s \n",s);
}

extern FILE * yyin;
extern FILE * yyout;

int main (int argc, char *argv[])
{
    /*
    lex peanut.l
    yacc -d peanut.y
    gcc -o demo y.tab.c lex.yy.c map.c symbol.c base_func.c parse_code.c  -ll -lm
    
    execute.c
        put_code(PUSH_NAME);
        put_data_w($1);
    */
    bp= (Byte *)calloc(GAPCODE, sizeof(Byte) );
    yyin = fopen(argv[1], "r");
    yyparse();
    put_code(FILE_OVER);
    initVM(bp, cursor);
    return 0;
}